package sess

import (
	"bytes"
	"context"
	"errors"
	"fmt"
	"net"
	"net/http"
	"strings"
	"sync"
	"time"

	"gitee.com/night-tc/gobige/global"
	"gitee.com/night-tc/gobige/logger"
	"gitee.com/night-tc/gobige/msgdef/protomsg"
	"gitee.com/night-tc/gobige/msghandler"
	"gitee.com/night-tc/gobige/pool"
	"gitee.com/night-tc/gobige/threads"
	"github.com/gorilla/websocket"
	"github.com/xtaci/kcp-go"
)

type optionF func(*NetSrv)

// 设置是否为管理客户端会话，默认为false
func NetSrvSetIsClient(v bool) optionF {
	return func(ns *NetSrv) {
		ns.isClient = v
	}
}

// 设置是否加密,默认为false；isClient为true时有效果
func NetSrvSetEnabled(v bool) optionF {
	return func(ns *NetSrv) {
		ns.encryEnabled = v
	}
}

// 设置监听服务类型 ProtocalEnum，默认为tcp
func NetSrvSetProtocal(v ProtocalEnum) optionF {
	return func(ns *NetSrv) {
		ns.protocal = v
	}
}

// 设置链接管理器的消息路由
func NetSrvSetMsgHander(v *msghandler.MsgHandler) optionF {
	return func(ns *NetSrv) {
		ns.MsgHandler = v
	}
}

// // 通过配置表加载配置
// func NetSrvSetConfig(v *global.ListenCfg) optionF {
// 	return func(ns *NetSrv) {
// 		ns.Cfg = v
// 		// ns.listenAddr = v.Listen
// 		// ns.maxConns = v.MaxConn
// 	}
// }

/*
网络连接的监听器
处理监听，管理连接会话，基础的连接验证
*/
type NetSrv struct {
	*msghandler.MsgHandler
	//子类对象引用
	realPtr INetServer

	//管理所有连接过来的会话
	sessesByID sync.Map
	//协程管理器
	thgo *threads.ThreadGo

	//是否加密
	encryEnabled bool
	//是否为客户端会话
	isClient bool

	/* 网络管理器 */

	//监听服务
	listener net.Listener
	//tcp还是KCP
	protocal ProtocalEnum
	// //连接数上限(<=0表示无上限)
	// maxConns int

	//监听地址
	ListenAddr string

	// 服务器配置
	Cfg *global.ListenCfg
}

func NewNetServer(cfg *global.ListenCfg, thgo *threads.ThreadGo, realPtr INetServer, opts ...optionF) (result *NetSrv) {
	result = new(NetSrv)
	result.MsgHandler = msghandler.NewMsgHandler()
	if realPtr == nil {
		result.realPtr = result
	} else {
		result.realPtr = realPtr
	}

	result.thgo = threads.NewThreadGoByGo(thgo)

	result.encryEnabled = false
	result.isClient = false

	result.protocal = Protocal_Tcp
	result.Cfg = cfg
	// result.maxConns = 0
	// result.listenAddr = addr
	for _, f := range opts {
		f(result)
	}
	if !result.isClient {
		result.encryEnabled = false
	}
	//监听端口
	var port int
	switch result.protocal {
	case Protocal_Tcp, Protocal_WebSocket:
		result.listener, port = GetListener("tcp", result.Cfg)
		// result.listener, result.Prot = net.Listen("tcp", result.listenAddr)
	case Protocal_Kcp:
		result.listener, port = GetListener("kcp", result.Cfg)
		// result.listener, err = kcp.Listen(result.listenAddr)
	default:
		err := Err_Unknown_ProtocalEnum.NewErr(nil, result.protocal)
		logger.Error(err)
		panic(err)
	}
	result.ListenAddr = fmt.Sprintf("%s:%d", result.Cfg.Addr, port)
	logger.Infof("Net server listen addr:%s;protocal:%s", result.ListenAddr, result.protocal)

	return
}

////////////////////////////////////////////////////////////////////
// 私有函数

// 监听方式获取链接
func (this *NetSrv) acceptConn(ctx context.Context) {
	for {
		select {
		case <-ctx.Done():
			return
		default:
			{
				conn, err := this.listener.Accept()
				if err != nil {
					if strings.Contains(err.Error(), "use of closed network connection") {
						return
					}
					logger.Error("accept connection error ", err)
					continue
				}
				if this.protocal == "kcp" {
					udpSession := conn.(*kcp.UDPSession)
					if udpSession != nil {
						udpSession.SetStreamMode(true)
						udpSession.SetWindowSize(10*1024, 10*1024)
						udpSession.SetNoDelay(1, 10, 2, 1)
						_ = udpSession.SetDSCP(46)
						udpSession.SetMtu(1400)
						udpSession.SetACKNoDelay(false)
					}
				}
				this.newSess(conn)
				// go this.receiver.acceptConn(conn)
			}
		}
	}
}

func (this *NetSrv) websocketacceptConn(ctx context.Context) {
	mux := http.NewServeMux()
	mux.HandleFunc("/ws", func(w http.ResponseWriter, r *http.Request) {
		upgrader := websocket.Upgrader{
			CheckOrigin: func(r *http.Request) bool {
				return true
			},
		}
		conn, err := upgrader.Upgrade(w, r, nil)
		if err != nil {
			logger.Error("websocket upgrade error ", err)
			return
		}
		this.websocketNew(conn)
	})
	httpSrv := &http.Server{
		Handler: mux,
	}
	err := httpSrv.Serve(this.listener)
	if err != nil {
		if !strings.Contains(err.Error(), "use of closed network connection") && !errors.Is(err, http.ErrServerClosed) {
			panic(err)
		}
	}
}

// websocket方式获取新链接
func (this *NetSrv) websocketNew(conn *websocket.Conn) {
	wsconn := webSocket{
		Conn:   conn,
		buffer: bytes.NewBuffer(pool.Get(MaxMsgBuffer)),
	}
	wsconn.buffer.Reset()
	this.newSess(&wsconn)
	// 使用这个库，如果不卡着协程，链接会被关闭
	// sess.thgo.Wg.Wait()
}

// 开一个新的连接
func (this *NetSrv) newSess(conn net.Conn) *NetSess {
	sess := newNetSess(conn, this.realPtr, this.thgo, false, this.encryEnabled)
	sess.SetMsgHandler(this.MsgHandler)
	sess.Start()
	return sess
}

// 把连接放进集合中
func (this *NetSrv) PutSess(sess INetSess) {
	for {
		if v, ok := this.sessesByID.LoadOrStore(sess.GetID(), sess); ok {
			oldsess := v.(INetSess)
			oldsess.Send(&protomsg.UserDuplicateLoginNtf{})
			logger.Infof("UserDuplicateLoginNtf pid:%d;old:[%s];new:[%s];", sess.GetID(), oldsess.RemoteAddr(), sess.RemoteAddr())
			this.removeSess(oldsess)
			oldsess.DelayClose()
		} else {
			return
		}
	}
}

// 删除连接
func (this *NetSrv) removeSess(sess INetSess) {
	this.sessesByID.CompareAndDelete(sess.GetID(), sess)
	sess.SetMsgHandler(nil)
}

/* 私有函数 */
////////////////////////////////////////////////////////////////////

type webSocket struct {
	Conn   *websocket.Conn
	buffer *bytes.Buffer
}

func (this *webSocket) Read(b []byte) (n int, err error) {
	if this.buffer.Len() == 0 {
		this.buffer.Reset()
		_, buf, err := this.Conn.ReadMessage()
		if err != nil {
			return 0, err
		}
		this.buffer.Write(buf)
	}
	return this.buffer.Read(b)
}
func (this *webSocket) Write(b []byte) (n int, err error) {
	err = this.Conn.WriteMessage(websocket.BinaryMessage, b)
	return len(b), err
}
func (this *webSocket) Close() error {
	return this.Conn.Close()
}
func (this *webSocket) LocalAddr() net.Addr {
	return this.Conn.LocalAddr()
}
func (this *webSocket) RemoteAddr() net.Addr {
	return this.Conn.RemoteAddr()
}
func (this *webSocket) SetDeadline(t time.Time) error {
	return this.Conn.NetConn().SetDeadline(t)
}
func (this *webSocket) SetReadDeadline(t time.Time) error {
	return this.Conn.SetReadDeadline(t)
}
func (this *webSocket) SetWriteDeadline(t time.Time) error {
	return this.Conn.SetWriteDeadline(t)
}

////////////////////////////////////////////////////////////////////
